package com.free.mini.gateway.filter;

import java.net.URI;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

import com.free.mini.common.utils.BizTraceUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;

import com.free.mini.common.base.ResultData;
import com.free.mini.gateway.constants.GatewayConstant;

import lombok.Data;
import lombok.experimental.Accessors;

/**
 *
 */
@Data
@Accessors(chain = true)
public class GatewayContext {

    public GatewayContext(ServerWebExchange serverWebExchange) {
        this.serverWebExchange = serverWebExchange;
        HttpHeaders headers = this.getServerHttpRequest().getHeaders();
        String contentType = headers.getFirst(GatewayConstant.CONTENT_TYPE);
        String[] contentTypes = StringUtils.isNotBlank(contentType) ? contentType.split(",") : null;
        if (contentTypes != null && contentTypes.length > 1) {
            setHeaders(GatewayConstant.CONTENT_TYPE, contentType.split(",")[0]);
        }
        this.accessToken = headers.getFirst(GatewayConstant.TOKEN);
        this.platform = headers.getFirst(GatewayConstant.PLATFORM);
        this.appVersionCode = headers.getFirst(GatewayConstant.APP_VERSION_CODE);
        String ts = headers.getFirst(GatewayConstant.TIMESTAMP);
        if (StringUtils.isNotBlank(ts) && StringUtils.isNumeric(ts)) {
            this.timestamp = Long.valueOf(ts).longValue();
        }
        this.nonceId = headers.getFirst(GatewayConstant.NONCE);
        startDate = new Date();
        startTime = System.currentTimeMillis();
        this.setAttributes(GatewayConstant.CACHE_GATEWAY_CONTEXT, this);
    }

    /**
     * 平台，h5|android|ios
     */
    private String platform;
    private String appVersionCode;
    private long timestamp = 0;
    private String nonceId;
    /**
     * 是否需要加解密
     */
    private Boolean needEncryption = true;

    private Date startDate;
    private long startTime;

    private Date endDate;
    private long endTime;

    /**
     * 是否登录
     */
    private boolean login = false;
    private boolean skipLogin = false;

    /**
     * gateway serverWebExchange
     */
    private ServerWebExchange serverWebExchange;

    /**
     * serverWebExchange
     */
    private GatewayFilterChain gatewayFilterChain;

    /**
     * request
     */
    private ServerHttpRequest serverHttpRequest;

    /**
     * response
     */
    private ServerHttpResponse serverHttpResponse;

    /**
     * 解密后params
     */
    private Map<String,Object> params;

    /**
     * cache form data
     */
    private MultiValueMap<String, String> formData;

    /**
     * 解密前params
     */
    private String encodeParams;

    /**
     * cache request path
     */
    private String path;

    /**
     * headers
     */
    private HttpHeaders headers;

    private String accessToken;

    /**
     * userId
     */
    private String userId;

    /**
     * mobile
     */
    private String mobile;
    private String email;
    private String account;
    private String accountType;

    /**
     * bizTraceId
     */
    private String bizTraceId;

    /**
     * ContentType
     */
    private MediaType contentType;

    /**
     * HttpMethod
     */
    private HttpMethod method;

    private ResultData result;

    public GatewayContext setBizTraceId(String bizTraceId) {
        this.bizTraceId = bizTraceId;
        return this.setHeaders(BizTraceUtil.BIZ_TRACE_ID, bizTraceId);
    }

    public URI getURI() {
        return this.getServerHttpRequest().getURI();
    }

    public ServerHttpResponse getServerHttpResponse() {
        return serverHttpResponse != null ? serverHttpResponse
                : (serverHttpResponse = this.getServerWebExchange().getResponse());
    }

    public HttpMethod getMethod() {
        return method != null ? method : (method = this.getServerHttpRequest().getMethod());
    }

    public MediaType getContentType() {
        return contentType != null ? contentType : (contentType = this.getHeaders().getContentType());
    }

    public String getBizTraceId() {
        return bizTraceId != null ? bizTraceId : (bizTraceId = this.getHeaders().getFirst(BizTraceUtil.BIZ_TRACE_ID));
    }

    public HttpHeaders getHeaders() {
        return headers != null ? headers : (headers = this.getServerHttpRequest().getHeaders());
    }

    public String getFirstHeader(String k) {
        return this.getHeaders().getFirst(k);
    }

    public List<String> getHeader(String k) {
        return this.getHeaders().get(k);
    }

    public ServerHttpRequest getServerHttpRequest() {
        return serverHttpRequest != null ? serverHttpRequest
                : (serverHttpRequest = this.getServerWebExchange().getRequest());
    }

    public String getPath() {
        return path != null ? path : (path = this.getServerHttpRequest().getPath().pathWithinApplication().value());
    }

    public GatewayContext setAttributes(String k, Object v) {
        this.getServerWebExchange().getAttributes().put(k, v);
        return this;
    }

    public GatewayContext setHeaders(String k, String v) {
        Consumer<HttpHeaders> httpHeaders = httpHeader -> httpHeader.set(k, v);
        ServerHttpRequest serverHttpRequest = this.getServerHttpRequest().mutate().headers(httpHeaders).build();
        this.getServerWebExchange().mutate().request(serverHttpRequest).build();
        return this;
    }

    public GatewayContext setContentLength(long contentLength) {
        Consumer<HttpHeaders> httpHeaders = httpHeader -> httpHeader.setContentLength(contentLength);
        ServerHttpRequest serverHttpRequest = this.getServerHttpRequest().mutate().headers(httpHeaders).build();
        this.getServerWebExchange().mutate().request(serverHttpRequest).build();
        return this;
    }

    public GatewayContext removeContentLength() {
        this.getServerHttpRequest().mutate().headers(httpHeaders -> {
            httpHeaders.remove(GatewayConstant.CONTENT_LENGTH);
            httpHeaders.add(GatewayConstant.TRANSFER_ENCODING, GatewayConstant.CHUNKED);
        }).build();
        return this;
    }

}